﻿Imports System.Text
Imports System.Reflection

Public Class ACImpDocumento(Of T)
#Region " Variables "

   Private m_impresion As ACImprimir
   Private m_cabecera As ACCabecera
   Private m_cuerpo As ACCuerpo(Of T)
   Private m_pie As ACCabecera
   Private m_header As Integer
   Private m_footer As Integer
   Private m_left As Integer
   Private m_right As Integer

#End Region

#Region " Propiedades "
   Public Property Header() As Integer
      Get
         Return m_header
      End Get
      Set(ByVal value As Integer)
         m_header = value
      End Set
   End Property

   Public Property Footer() As Integer
      Get
         Return m_footer
      End Get
      Set(ByVal value As Integer)
         m_footer = value
      End Set
   End Property

   Public Property Left() As Integer
      Get
         Return m_left
      End Get
      Set(ByVal value As Integer)
         m_left = value
      End Set
   End Property

   Public Property Right() As Integer
      Get
         Return m_right
      End Get
      Set(ByVal value As Integer)
         m_right = value
      End Set
   End Property

   Public Property Pie() As ACCabecera
      Get
         Return m_pie
      End Get
      Set(ByVal value As ACCabecera)
         m_pie = value
      End Set
   End Property

   Public Property Cuerpo() As ACCuerpo(Of T)
      Get
         Return m_cuerpo
      End Get
      Set(ByVal value As ACCuerpo(Of T))
         m_cuerpo = value
      End Set
   End Property

   Public Property Cabecera() As ACCabecera
      Get
         Return m_cabecera
      End Get
      Set(ByVal value As ACCabecera)
         m_cabecera = value
      End Set
   End Property

   Public Property Impresion() As ACImprimir
      Get
         Return m_impresion
      End Get
      Set(ByVal value As ACImprimir)
         m_impresion = value
      End Set
   End Property

#End Region

#Region " Constructores "
   Public Sub New()

   End Sub

   Public Sub New(ByVal x_cabecera As ACCabecera)

   End Sub

   Public Sub New(ByVal x_cabecera As ACCabecera, ByVal x_cuerpo As ACCuerpo(Of T), ByVal x_pie As ACCabecera)
      Try
         m_cabecera = x_cabecera
         m_cuerpo = x_cuerpo
         m_pie = x_pie
      Catch ex As Exception
         Throw ex
      End Try
   End Sub

#End Region

#Region " Metodos "
   Public Function Ejecutar() As StringBuilder
      Dim m_documento As New StringBuilder
      Try
         Ejecutar(m_documento)
         Return m_documento
      Catch ex As Exception
         Throw ex
      End Try
   End Function

   Public Sub Ejecutar(ByRef m_documento As StringBuilder)
      Try
         Dim x_cabecera As New StringBuilder
         genCabecera(x_cabecera)
         Dim x_cuerpo As New StringBuilder
         genCuerpo(x_cuerpo)
         Dim x_pie As New StringBuilder
         genPie(x_pie)

         m_documento.Append(x_cabecera)
         m_documento.Append(x_cuerpo)
         m_documento.Append(x_pie)
      Catch ex As Exception
         Throw ex
      End Try
   End Sub

   Private Sub genCabecera(ByVal x_cabecera As StringBuilder)
      Try
         Dim i As Integer = 0
         For Each Item As String In m_cabecera.Texto
            x_cabecera.Append(String.Format("{0}{1}", Item, vbNewLine))
            i += 1
         Next
         ImprimeLinBlanco(x_cabecera, m_cabecera.Lineas - i)
      Catch ex As Exception
         Throw ex
      End Try
   End Sub

   Private Sub genPie(ByVal x_pie As StringBuilder)
      Try
         Dim i As Integer = 0
         For Each Item As String In m_pie.Texto
            x_pie.Append(String.Format("{0}{1}", Item, vbNewLine))
            i += 1
         Next
         ImprimeLinBlanco(x_pie, m_cabecera.Lineas - i)
      Catch ex As Exception
         Throw ex
      End Try
   End Sub

   Private Sub genCuerpo(ByRef x_cuerpo As StringBuilder)
      Try
         '' colocar cabeceras
         Dim x_header As String = "" & Space(Left)
         For Each Item As ACColumna In m_cuerpo.Columnas
            x_header &= Item.Titulo.PadRight(Item.Ancho, " ").Substring(0, Item.Ancho)
         Next
         x_header &= vbNewLine

         '' Cargar el detalle
         Const _error As String = "La Propiedad ""{0}"" no existe en el tipo ""{1}"""
         Dim i As Integer = 1
         Dim x_body As New StringBuilder
         x_body.Append(Space(m_left))
         If m_cuerpo.Cabecera Then
            For Each Item As ACColumna In m_cuerpo.Columnas
               x_body.Append(Item.Titulo.PadRight(Item.Ancho, " ").Substring(0, Item.Ancho))
            Next
         End If
         x_body.Append(vbNewLine)
         Dim _fila As Integer = 1
         For Each Registro As T In m_cuerpo.ListadoColumnas
            x_body.Append(Space(m_left))
            For Each Item As ACColumna In m_cuerpo.Columnas
               Dim _tipo As Type = GetType(T)
               Dim _info As PropertyInfo
               _info = _tipo.GetProperty(Item.Nombre)
               Dim _tipoCampo As Type = _info.PropertyType
               If _info Is Nothing Then
                  Throw New MissingFieldException(String.Format(_error, Item.Nombre, _tipo.ToString()))
               Else
                  Dim _valor As Object = _info.GetValue(Registro, Nothing)
                  x_body.Append(Alinear(Item.Alineacion, SetFormato(_valor, _tipoCampo.ToString(), Item.Formato), Item.Ancho))
               End If
            Next
            x_body.Append(vbNewLine)
            i += 1 : _fila += 1
         Next
         x_cuerpo.Append(x_body)
         ImprimeLinBlanco(x_cuerpo, m_cuerpo.Filas - _fila)

      Catch ex As Exception
         Throw ex
      End Try
   End Sub
#End Region

#Region " Metodos Privados "
   Private Shared Function SetFormato(ByVal x_valor As Object, ByVal _tipoCampo As String, ByVal x_formato As String) As String
      Try
         Select Case _tipoCampo.ToString()
            Case "System.String"
               Return x_valor.ToString()
            Case "System.Int16", "System.Nullable`1[System.Int16]", "System.Int32", "System.Nullable`1[System.Int32]", "System.Int64", "System.Nullable`1[System.Int64]", _
                 "System.Double", "System.Nullable`1[System.Double]", "System.Decimal", "System.Nullable`1[System.Decimal]", "System.Long", "System.Nullable`1[System.Long]"
               If IsNumeric(x_valor) Then
                  Return Convert.ToDecimal(x_valor).ToString(x_formato)
               Else
                  Return x_valor.ToString()
               End If
            Case "System.DateTime", "System.Nullable`1[System.DateTime]"
               If x_formato.Equals("") Then
                  Return CType(x_valor, DateTime).ToString(x_formato)
               Else
                  Return CType(x_valor, DateTime).ToString()
               End If
            Case "System.Boolean", "System.Nullable`1[System.Boolean]"
               Return x_valor.ToString()
            Case "System.Xml.XmlDocument"
               Return x_valor.ToString()
            Case Else
               Return x_valor.ToString()
         End Select
      Catch ex As Exception
         Throw ex
      End Try
   End Function

   Private Shared Function Alinear(ByVal alig As ACAlineacion, ByVal cadena As String, ByVal ancho As Integer) As String
      Try
         Dim width As Integer
         Select Case alig
            Case ACAlineacion.Centro
               If Not cadena Is Nothing Then
                  If ancho < cadena.Length Then
                     width = ancho
                  Else
                     width = Math.Truncate((ancho - cadena.Length) / 2)
                  End If
               Else
                  width = 0
               End If
               Dim text As String = ""
               text = "".PadLeft(width, " ") & cadena
               cadena = text.PadRight(ancho, " ").Substring(0, ancho)
            Case ACAlineacion.Derecha
               cadena = cadena.ToString().PadLeft(ancho, " ").Substring(0, ancho)
            Case ACAlineacion.Izquierda
               cadena = cadena.ToString().PadRight(ancho, " ").Substring(0, ancho)
            Case Else
               cadena = cadena.ToString().PadLeft(ancho, " ").Substring(0, ancho)
         End Select
         Return cadena
      Catch ex As Exception
         Throw ex
      End Try
   End Function

   Private Shared Sub ImprimeLinBlanco(ByRef _text As StringBuilder, ByVal _line As Integer)
      Dim n As Integer
      For n = 1 To _line
         _text.Append(vbNewLine)
      Next
   End Sub
#End Region

End Class

